<?php

// Namespace

namespace Loop;


/**
 * CImageHelper is a class that provides utility functions to manipulate images
 *
 * @package		Loop
 * @category	Helper
 */

class CImageHelper
{
	/**
	 * Crop an image
	 * 
	 * @param 	string 			$pathToSource		The source path
	 * @param 	string 			$pathToTarget		The target path
	 * @param 	int 			$targetWidth 		The target width
	 * @param 	int 			$targetHeight 		The target height
	 */
	
	public function crop($pathToSource, $pathToTarget, $targetWidth, $targetHeight)
	{
		// Get functions related to the image type
		
		$this->getImageType($pathToSource, $functionCreateFrom, $functionImage);
		
		
		// Load the source image
		
		$sourceImage = $functionCreateFrom($pathToSource);

		
		// Extract the dimensions of the source image
		
		$sourceWidth = imagesx($sourceImage);
		$sourceHeight = imagesy($sourceImage);


		// Resize original image so that it perfectly fits within the cropped image

		if ($sourceWidth > $sourceHeight)
		{
			$resizedWidth = $targetHeight * ($sourceWidth / $sourceHeight);
			$resizedImage = imagecreatetruecolor($resizedWidth, $targetHeight);

			imagecopyresampled($resizedImage, $sourceImage, 0, 0, 0, 0, $resizedWidth, $targetHeight, $sourceWidth, $sourceHeight);

			$sourceWidth = $resizedWidth;
			$sourceHeight = $targetHeight;
		}
		else
		{
			$resizedHeight = $targetWidth * ($sourceHeight / $sourceWidth);
			$resizedImage = imagecreatetruecolor($targetWidth, $resizedHeight);

			imagecopyresampled($resizedImage, $sourceImage, 0, 0, 0, 0, $targetWidth, $resizedHeight, $sourceWidth, $sourceHeight);

			$sourceHeight = $resizedHeight;
			$sourceWidth = $targetWidth;
		}
		
		
		// Define the center of the source image
		
		$Ox = $sourceWidth / 2;
		$Oy = $sourceHeight / 2;
		
		
		// Define points from which we'll start and stop copying the source into the target
		
		$Ax = $Ox - ($targetWidth / 2);
		$Ay = $Oy - ($targetHeight / 2);
		
		$Bx = $Ax + $targetWidth;
		$By = $Ay + $targetHeight;
		
		
		// Create the target image at the expected dimensions
		
		$targetImage = imagecreatetruecolor($targetWidth, $targetHeight);
		
		
		// Setup an alpha channel
		
		imagealphablending($targetImage, false);
		imagesavealpha($targetImage, true);
		
		
		// Copy the source into the target
		
		imagecopyresampled
		(
			$targetImage,
			$resizedImage,
			0, 0, $Ax, $Ay,
			$sourceWidth, $sourceHeight, $Bx, $By
		);
		
		$functionImage($targetImage, $pathToTarget);


		// Destroy temporary images

		imagedestroy($sourceImage);
		imagedestroy($resizedImage);
		imagedestroy($targetImage);
	}


	/**
	 * Generate Bar Code
	 * 
	 * @param 				$barcodeValue	
	 * @param 	string 		$pathToFile 	The path to file
	 */

	public function generateBarcode($barcodeValue, $pathToFile)
	{
		// Dependencies

		require_once(PATH_LOOP . 'Component/Barcode/5.0.1/class/BCGColor.php');
		require_once(PATH_LOOP . 'Component/Barcode/5.0.1/class/BCGDrawing.php');
		require_once(PATH_LOOP . 'Component/Barcode/5.0.1/class/BCGcode128.barcode.php');


		// Setup colors

		$colorOfForeground = new BCGColor(0, 0, 0);
		$colorOfBackground = new BCGColor(255, 255, 255);


		// Create the barcode

		$barcode = new BCGcode128();

		$barcode->setScale(2);
		$barcode->setThickness(30);
		$barcode->parse($barcodeValue);
		$barcode->setForegroundColor($colorOfForeground);
		$barcode->setBackgroundColor($colorOfBackground);
		$barcode->setLabel(null);


		// Draw the barcode and write it inside the file

		$barcodeDrawing = new BCGDrawing($pathToFile, $colorOfBackground);
		$barcodeDrawing->setBarcode($barcode);
		$barcodeDrawing->draw();
		$barcodeDrawing->finish(BCGDrawing::IMG_FORMAT_PNG);
	}
	
	
	/**
	 * Generate An image in gray color
	 * 
	 * @param 	string 			$pathToSource	The source path
	 * @param 	string 			$pathToTarget	The target path
	 */
	
	public function generateGrayImage($pathToSource, $pathToTarget)
	{
		// Get the type of image

		$this->getImageType($pathToSource, $functionCreateFrom, $functionImage);
		

		// Create the colored image

		$coloredImage = $functionCreateFrom($pathToSource);
		

		// Get its dimensions

		$width = imagesx($coloredImage);
		$height = imagesy($coloredImage);
		

		// Create the gray image

		$grayImage = imagecreate($width, $height);
		

		// Create a gray palette

		for ($i = 0; $i <= 255; $i++)
		{
		    $palette[$i] = imagecolorallocate($grayImage, $i, $i, $i); 
		} 
		

		// Convert each colored point into a gray one

		for ($x = 0; $x < $width; $x++)
		{
			for ($y = 0; $y < $height; $y++)
			{
		        $rgb = imagecolorat($coloredImage, $x, $y);
		        
		        $r = ($rgb >> 16) & 0xFF;
		        $g = ($rgb >> 8) & 0xFF;
		        $b = $rgb & 0xFF;
		        
		        imagesetpixel($grayImage, $x, $y, $palette[0.199 * $r + 0.587 * $g + 0.114 * $b]); 
		    } 
		} 
		

		// Save the gray image

		$functionImage($grayImage, $pathToTarget);


		// Destroy temporary images

		imagedestroy($coloredImage);
		imagedestroy($grayImage);
	}
	
	
	/**
	 * Generate gray icons 
	 * 
	 * @param 	int  $index 	The index of the file
	 */
	
	public function generateIcons($index = null)
	{
		// Generate icons for each of the defined sizes

		$sizes = array(16, 24, 32, 48, 64, 128, 256, 512);
		
		foreach ($sizes as $size)
		{
			// Create colored icon
			
			$pathToSource = $this->getFilePath($this->defaultIcon, 'thumbnail', null, $index, $size);
			$colorPath = $this->getFilePath($this->defaultIcon, 'icon', 'color', null, $size);
			
			$this->resize($pathToSource, $colorPath, $size, $size);
			$this->crop($pathToSource, $colorPath, $size, $size);
			
			
			// Create gray icon
			
			$grayPath = $this->getFilePath($this->defaultIcon, 'icon', 'gray', null, $size);
			
			$this->generateGrayImage($colorPath, $grayPath);
		}
	}

	
	/**
	 * Generates a QR code
	 *
	 * @param	string	$qrCodeValue	The value of the QR code
	 * @param	string	$pathToFile		The path to which the QR code must be saved
	 */
	
	public function generateQrCode($qrCodeValue, $pathToFile)
	{
		// Dependencies
		
		require_once(PATH_LOOP . 'Component/PhpQrCode/qrlib.php');
		
		
		// Generate the QR code
		
		QRcode::png($qrCodeValue, $pathToFile, 'H', 10, 2);
	}
	
	
	/**
	 * Generate object thumbnails
	 *
	 * @param 	string		$pathToSource 		The source path
	 * @param  	object		$object 			The object
	 * @param 	string 		$propertyName		The name of the property
	 * @param 	int			$index 				The index of the file
	 * @param 	string 		$class 				The class of the image
	 * @param 				$variant
	 * @param 	resource 	$size 				can be null, an integer or an array with width and height
	 * @param 	string		$fileExtension 		The extension of file
	 */
	
	public function generateThumbnails($pathToSource, $object, $propertyName, $index, $class, $variant, $size, $fileExtension)
	{
		// by default, thumbnail is generated in all formats

		$sizes = array(16, 24, 32, 48, 64, 128, 256, 512);

		if (is_array($size) == false)
		{			
			if (in_array($size, $sizes) == true)
			{
				$sizes = array($size);
			}
		}
		else if (count($size) == 2)
		{
			// Expected 2 values, width and height

			$sizes = $size;
		}


		$pathToExtension = app()->getPathHelper()->convertExtensionIdToPath($object->extensionId);


		// If the extension is the current application, we must add application folder.

		if ($pathToExtension == PATH_ROOT)
		{
			$pathToExtension .= 'Application/';
		}


		// Get the reference if exists.

		try
		{
			$reference = $object->getReference();
		}
		catch (\Exception $e)
		{
			$reference = null;
		}
		
		
		// Define target path.

		if (count($sizes) != 2)
		{
			foreach ($sizes as $size)
			{
				$pathToTarget = $object->getPathToFile($propertyName, $index, $class, 'thumbnail', $size, $fileExtension);
				
				$this->resize($pathToSource, $pathToTarget, null, $size);
			}
		}
		else
		{
			$pathToTarget = $object->getPathToFile($propertyName, $index, $class, 'thumbnail', $sizes[0] . 'x' . $sizes[1], $fileExtension);

			$this->resize($pathToSource, $pathToTarget, $sizes[0], $sizes[1]);
		}
	}
	
	
	/**
	 * Set the appropriate type of image and the appropriate function to use for this type of image
	 * 
	 * @param 	string 		$pathToSource			The source path
	 * @param 	string 		$functionCreateFrom  	The appropriate function to use to create image
	 * @param 	string 		$functionImage			The appropriate function to use to output the image	
	 */
	
	public function getImageType($pathToSource, &$functionCreateFrom, &$functionImage)
	{
		// Define which functions must be used to process the image
		// As exif_imagetype() is not well supported, offer an alternative way to find the type

		if (function_exists('exif_imagetype') == true)
		{
			$imageType = exif_imagetype($pathToSource);

			if ($imageType == IMAGETYPE_PNG)
			{
				$functionCreateFrom = 'imagecreatefrompng';
				$functionImage = 'imagepng';
			}
			else if($imageType == IMAGETYPE_GIF)
			{
				$functionCreateFrom = 'imagecreatefromgif';
				$functionImage = 'imagegif';	
			}
			else
			{
				$functionCreateFrom = 'imagecreatefromjpeg';
				$functionImage = 'imagejpeg';
			}
		}
		else
		{
			$fileFragments = explode('.', $pathToSource);
			$fileExtension = array_pop($fileFragments);

			if ($fileExtension == 'png')
			{
				$functionCreateFrom = 'imagecreatefrompng';
				$functionImage = 'imagepng';
			}
			else if($fileExtension == 'gif')
			{
				$functionCreateFrom = 'imagecreatefromgif';
				$functionImage = 'imagegif';	
			}
			else
			{
				$functionCreateFrom = 'imagecreatefromjpeg';
				$functionImage = 'imagejpeg';
			}
		}
	}
	
	
	/**
	 * Resize an image
	 * 
	 * @param 	string 	$pathToSource 		The source path
	 * @param 	string 	$pathToTarget		The target path
	 * @param 	float 	$targetWith 		The target width
	 * @param 	float 	$targetHeight		The target height
	 * @param 	boolean $keepAspectRatio	Keep the ratio on the new image target
	 */
	
	public function resize($pathToSource, $pathToTarget, $targetWidth = null, $targetHeight = null, $keepAspectRatio = true)
	{
		// Do nothing if no width and no height are specified
		
		if
		(
			(empty($targetWidth) == true) &&
			(empty($targetHeight) == true)
		)
		{
			return;
		}
		
		
		// Get functions related to the image type
		
		$this->getImageType($pathToSource, $functionCreateFrom, $functionImage);
		
		
		// Load the source image
		
		$sourceImage = $functionCreateFrom($pathToSource);

		
		// Get dimensions of the source image
		
		$sourceWidth = imagesx($sourceImage);
		$sourceHeight = imagesy($sourceImage);
		
		
		// Define the target height / width if one is not defined
		
		if ($targetWidth == null)
		{
			$targetWidth = ($sourceWidth / $sourceHeight) * $targetHeight;
		}
		else if ($targetHeight == null)
		{
			$targetHeight = ($sourceHeight / $sourceWidth) * $targetWidth;
		}


		// Aspect ratio must be kept as is

		if ($keepAspectRatio == true)
		{
			// Ensure target dimensions preserve the original aspect ratio
			
			if ($sourceWidth > $sourceHeight) 
			{ 
				$targetHeight = $targetWidth * ($sourceHeight / $sourceWidth);
			}
			else
			{ 
				$targetWidth = $targetHeight * ($sourceWidth / $sourceHeight);
			} 
		}
		
		
		// Create the target image with the right dimensions
		
		$targetImage = imagecreatetruecolor($targetWidth, $targetHeight);
		
		
		// Setup an alpha channel
		
		imagealphablending($targetImage, false);
		imagesavealpha($targetImage, true);
		
		
		// Copy the source image into the target image
		
		imagecopyresampled
		(
			$targetImage,
			$sourceImage,
			0, 0, 0, 0,
			$targetWidth, $targetHeight,
			$sourceWidth, $sourceHeight
		);
		
		
		// Save the target image into a file
		
		$functionImage($targetImage, $pathToTarget);

		chmod($pathToTarget, 0664);


		// Destroy temporary images

		imagedestroy($sourceImage);
		imagedestroy($targetImage);
	}


	/**
	 * Rotate an image with a given angle
	 * 
	 * @param 	string 		$pathToSource 	The source path
	 * @param 	string 		$pathToTarget	The target path
	 * @param 	float 		$angle 			Rotation angle, in degrees
	 */

	public function rotate($pathToSource, $pathToTarget, $angle)
	{
		// Get functions related to the image type
		
		$this->getImageType($pathToSource, $functionCreateFrom, $functionImage);
		
		
		// Create the image

		$sourceImage = $functionCreateFrom($pathToSource);


		// Rotate the image

		$targetImage = imagerotate($sourceImage, $angle, 0);


		// Save the target image into a file
		
		$functionImage($targetImage, $pathToTarget);


		// Destroy temporary images

		imagedestroy($sourceImage);
		imagedestroy($targetImage);
	}
}

?>
